#include "GBox.h"

#include "Object.h"

struct _GBox
{
        GWidget parent_instance;
        /* 子部件的方向 */
        GOrientation orientation;
        /* 第一个子部件 */
        GWidget *start_child;
        /* 最后一个小部件 */
        GWidget *end_child;
        /* GBox 背景颜色 */
        uint16_t background_color;
        /* 小部件的数量 */
        uint8_t number;
        /* 指示子部件是否该为相同大小 */
        bool is_homogeneous;
};

static uint16_t
g_box_get_min_width(const GBox *self)
{
        GWidget *widget = self->start_child;
        uint16_t width  = 0;

        while (widget) {
                const uint16_t tmpw = g_widget_get_min_width(widget);
                if (self->orientation == G_ORIENTATION_HORIZONTAL)
                        width += tmpw;
                else
                        width = width > tmpw ? width : tmpw;
                widget = G_WIDGET(widget)->next;
        }

        return width;
}

static uint16_t
g_box_get_min_height(const GBox *self)
{
        GWidget *widget = self->start_child;
        uint16_t height = 0;

        while (widget) {
                const uint16_t tmph = g_widget_get_min_height(widget);
                if (self->orientation == G_ORIENTATION_VERTICAL)
                        height += tmph;
                else
                        height = height > tmph ? height : tmph;
                widget = G_WIDGET(widget)->next;
        }

        return height;
}

static void
g_box_render_self(const uint16_t startX,
                  const uint16_t startY,
                  const uint16_t endX,
                  const uint16_t endY,
                  const uint16_t background_color)
{
        Screen *screen = screen_new_from_global();
        screen_set_draw_area(screen, startX, startY, endX, endY);
        for (uint16_t i = startX; i <= endX; ++i)
                for (uint16_t j = startY; j <= endY; ++j)
                        screen_draw_pixel(screen, background_color);
}

/**
 * @brief GBox 的渲染功能，最重要的是根据子部件的位置信息分配渲染区域
 */
void
g_box_render(GBox    *self,
             uint16_t startX,
             uint16_t startY,
             uint16_t endX,
             uint16_t endY)
{
        if (G_WIDGET(self)->force_render)
                G_WIDGET(self)->is_update = false;

        /* 重新渲染 GBox 本身 */
        if (!G_WIDGET(self)->is_update) {
                g_box_render_self(startX,
                                  startY,
                                  endX,
                                  endY,
                                  self->background_color);
                G_WIDGET(self)->is_update = true;
        }

        /* GBox.margin 重新计算 */
        startX += G_WIDGET(self)->margin;
        startY += G_WIDGET(self)->margin;
        endX -= G_WIDGET(self)->margin;
        endY -= G_WIDGET(self)->margin;

        GWidget *child = self->start_child;

        if (self->is_homogeneous == true)
                goto homogenerous_render;

        /* 小部件之间不保持相同大小 homogenerous == false */
        uint16_t msize;

        for (uint8_t b_child = 1; child; child = child->next, ++b_child) {
                if (G_WIDGET(self)->force_render)
                        g_widget_set_force_render(child);
                if (self->orientation == G_ORIENTATION_HORIZONTAL) {
                        msize = g_widget_get_min_width(child);
                        g_widget_render(child,
                                        startX,
                                        startY,
                                        b_child >= self->number
                                                ? endX - 1
                                                : startX + msize - 1,
                                        endY);
                        startX += msize;
                } else {
                        msize = g_widget_get_min_height(child);
                        g_widget_render(child,
                                        startX,
                                        startY,
                                        endX,
                                        b_child >= self->number
                                                ? endY - 1
                                                : startY + msize - 1);
                        startY += msize;
                }
        }
        G_WIDGET(self)->is_update = true;
        return;

homogenerous_render:
        /* 小部件间分配大小相同的渲染: homogenerous == true */
        for (uint8_t b_child = 0; child; child = child->next, ++b_child) {
                if (G_WIDGET(self)->force_render)
                        g_widget_set_force_render(child);
                if (self->orientation == G_ORIENTATION_HORIZONTAL) {
                        g_widget_render(child,
                                        startX + b_child * (endX - startX) /
                                                         self->number,
                                        startY,
                                        startX + (b_child + 1) *
                                                         (endX - startX) /
                                                         self->number,
                                        endY);
                } else {
                        g_widget_render(child,
                                        startX,
                                        startY + b_child * (endY - startY) /
                                                         self->number,
                                        endX,
                                        startY + (b_child + 1) *
                                                         (endY - startY) /
                                                         self->number);
                }
        }

        G_WIDGET(self)->is_update = true;
}

GWidget *
g_box_new(GOrientation orientation, const bool is_homogeneous)
{
        GBox *self = object_new(GBox);
        g_widget_init(G_WIDGET(self),
                      G_WIDGET_NUMS_CALLBACK(g_box_get_min_width),
                      G_WIDGET_NUMS_CALLBACK(g_box_get_min_height),
                      G_WIDGET_RENDER(g_box_render));
        self->orientation      = orientation;
        self->is_homogeneous   = is_homogeneous;
        self->start_child      = NULL;
        self->end_child        = NULL;
        self->number           = 0;
        self->background_color = 0xffff;
        return G_WIDGET(self);
}

void
g_box_append(GBox *self, GWidget *widget)
{
        if (!self->start_child) {
                self->start_child = widget;
                self->end_child   = widget;
        } else {
                G_WIDGET(self->end_child)->next = widget;
                self->end_child                 = widget;
        }
        self->number += 1;
}

void
g_box_set_background_color(GBox *self, uint16_t color)
{
        self->background_color = color;
}